const express = require('express'),
      bodyParser = require('body-parser'), // 处理POST请求
      uploader = require('express-fileupload'),
      CryptoJS = require('crypto-js'),
      ffmpegPath = require('@ffmpeg-installer/ffmpeg').path,
      ffmpeg = require('fluent-ffmpeg'),
      session = require('express-session'),
      { redisGet, redisSet } = require('./redis');

const { readFileSync, writeFileSync, appendFileSync, existsSync, unlinkSync, rmdirSync, readdirSync, mkdirSync, appendFile, unlink } = require('fs'),
      { resolve } = require('path');


const app = express(),
      PORT = 8000;

// 对传入的请求体进行解析 JSON、Raw、Text、urlencoded form
app.use(bodyParser.urlencoded({ extended: true })); // 可扩展
app.use(bodyParser.json()); // 处理JSON文件
app.use(uploader()); // 处理上传文件 file类型
app.use('/', express.static('videos', { // 设置访问路径 到 videos文件夹下
    setHeaders(res) { //设置跨域
        res.set('Access-Control-Allow-Origin', '*'); // 允许所有访问的源 -> 静态文件跨域
    }
}))

var sessionOpts = {
 // 设置密钥
    secret: 'a cool secret',
    resave: true,
    saveUninitialized: true,
    key: 'myapp_sid',
    cookie: { maxAge: 60 * 60 * 1000, secure: false }
}
app.use(session(sessionOpts))

ffmpeg.setFfmpegPath(ffmpegPath);

// http://localhost:8000/videos/xxx/xxx.m3u8


app.all('*', (req, res, next) => { // 设置所有请求跨域
    res.header('Access-Control-Allow-Origin', "*");
    res.header('Access-Control-Allow-Methods', 'POST,GET'); // 允许的方法
    next(); // 执行下一个中间件
})

const ALLOWED_TYPES = {
    'video/mp4': 'mp4',
    'video/ogg': 'ogg',
    'hls': 'm3u8'
},
    tempDir = resolve(__dirname, './temp/'),
    videoDir = resolve(__dirname, './videos/');

let timer, t;

app.post('/upload_video', (req, res) => { //上传请求

    const {
        name, type, size, chunkName
    } = req.body;

    const { chunk } = req.files,
          fileName = CryptoJS.MD5(name),
          tempFilesDir = tempDir + '/' + fileName;

    if(!chunk){
        res.send({
            code: 1001,
            msg: 'No file uploaded'
        })
        return;
    }

    if(!ALLOWED_TYPES[type]){
        res.send({
            code:1002,
            msg: 'The type is not allowed for uploading'
        })
        return;
    }
    
    if(!existsSync(tempFilesDir)){
        mkdirSync(tempFilesDir);
    }

    if(timer) {
        let curTime = new Date().getTime();
        if((curTime - (timer)) % 60 < 10){
            timer = new Date().getTime();
            clearTimeout(t); 
            t = setTimeout(() => {
                const fileList = readdirSync(tempFilesDir);
                fileList.forEach(chunk => {
                    const chunkPath = `${tempFilesDir}/${chunk}`;
                    unlinkSync(chunkPath)
                });
                rmdirSync(tempFilesDir);
            }, 15000)
        }
    } else {
        timer = new Date().getTime();
        t = setTimeout(() => {
            const fileList = readdirSync(tempFilesDir);
            fileList.forEach(chunk => {
                const chunkPath = `${tempFilesDir}/${chunk}`;
                unlinkSync(chunkPath)
            });
            rmdirSync(tempFilesDir);
        }, 15000)
    }
    

    writeFileSync(tempFilesDir + '/' + chunkName, chunk.data);

    res.send({
        code: 0,
        msg: 'Chunks are all uploaded',
        fileName: fileName.toString()
    });
    
})

app.post('/merge_video', (req, res) => { //合并请求
    
    timer = null;
    clearTimeout(t);
    t = null;

    const { fileName, type } = req.body,
          tempFilesDir = tempDir + '/' + fileName,
          videoFileDir = videoDir + '/' + fileName,
          fileList = readdirSync(tempFilesDir);
    
    if(!existsSync(videoFileDir)){
        mkdirSync(videoFileDir);
    }

    const mp4Path = `${videoFileDir}/${fileName}.${ALLOWED_TYPES[type]}`,
          hlsPath = `${videoFileDir}/${fileName}.${ALLOWED_TYPES['hls']}`;
    fileList.forEach(chunk => {
        const chunkPath = `${tempFilesDir}/${chunk}`,
              chunkContent = readFileSync(chunkPath);

        if(!existsSync(mp4Path)){
            writeFileSync(mp4Path, chunkContent);
        } else {
            appendFileSync(mp4Path, chunkContent);
        }

        unlinkSync(chunkPath)
    });

    rmdirSync(tempFilesDir);

    formatVideo(mp4Path, {
        videoCodec: 'libx264',
        format: 'hls',
        outputOptions: '-hls_list_size 0',
        outputOption: '-hls_time 5',
        output: hlsPath,
        onError (e){

            const fileList = readdirSync(videoFileDir);

            fileList.forEach(chunk => {
                const chunkPath = `${videoFileDir}/${chunk}`
                unlinkSync(chunkPath);
            });

            rmdirSync(videoFileDir);

            res.send({
                code: 1006,
                msg: e.message
            })
        },
        onEnd(){
            res.send({
                code: 0,
                msg: 'Upload successfully',
                videoSrc: `http://localhost:8000/${fileName}/${fileName}.${ALLOWED_TYPES['hls']}`
            })
        }
    })
})


function formatVideo(path, {
    videoCodec,
    format,
    outputOptions,
    outputOption,
    output,
    onError,
    onEnd
}){
    ffmpeg(path)
        .videoCodec(videoCodec)
        .format(format)
        .outputOptions(outputOptions)
        .outputOption(outputOption)
        .output(output)
        .on('error', onError)
        .on('end', onEnd)
        .run();
}


app.listen(PORT, () => {
    console.log('Server is running on ' + `http://localhost:${PORT}`);
})